﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web;
using System.Collections.Specialized;
using System.IO;
using Guru.Utilities.Cryptography;
using System.Web.SessionState;
using System.Web.Security;
using System.Web.UI.WebControls;
using Guru.Utilities;


namespace Guru.Web.UI
{
    public abstract class GuruPage : System.Web.UI.Page
    {      
        protected enum GuruPageType
        {
            None,
            Normal,
            Modal
        }

        #region properties

        private HttpRequest _PageRequest;

        public NameValueCollection SecureQueryString { get; private set; }
        const string PARAM = "param";
        const string SPARAM = "sparam";
        public bool IsSecureQueryString { get { return Request[PARAM] != null; } }
        public bool IsSecureQueryStringStatic { get { return Request[SPARAM] != null; } }

        private GuruPageType _PageType = GuruPageType.Normal;
        protected GuruPageType PageType
        {
            get { return _PageType; }
            set { _PageType = value; }
        }

        private bool _IsIntruderDetected;

        public bool IsNoCache { get; set; }



        public CryptoServices Cryptor
        {
            get { return new CryptoServices(); }
        }

        public string PostData { get { return new StreamReader(_PageRequest.InputStream).ReadToEnd(); } }

        #endregion

        #region public method
        public bool IsUserWithPermission(string per_code)
        {
            if (HttpContext.Current.Session["UserPermissions"] == null) return false;
            string[] per_codes = (string[])HttpContext.Current.Session["UserPermissions"];
            return per_codes.FirstOrDefault<string>(obj => obj == per_code) != null;
        }

        public string BuildSecureQueryString(string query)
        {
            return "param=" + HttpUtility.UrlEncode(Cryptor.EncryptText(query, Session.SessionID.Substring(0, 8)));
        }
        public const string STATIC_KEY = "!*&@#^%$";
        public string BuildStaticQueryString(string query)
        {
            return "sparam=" + HttpUtility.UrlEncode(Cryptor.EncryptText(query, STATIC_KEY));
        }
        #endregion

        #region override methods

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            OnPageInit();
        }

        protected override void OnPreInit(EventArgs e)
        {
            base.OnPreInit(e);

            if (IsSecureQueryString || IsSecureQueryStringStatic)
            {
                string encryptedQueryStr = "";
                if (IsSecureQueryString) encryptedQueryStr = Request[PARAM];
                else if (IsSecureQueryStringStatic) encryptedQueryStr = Request[SPARAM];

                if (string.IsNullOrEmpty(encryptedQueryStr))
                {
                    _IsIntruderDetected = true;
                    RaiseIntruderEvent(IntruderType.SecureQueryString);
                }
                try
                {
                    string decryptedQueryStr = "";
                    if (IsSecureQueryString) decryptedQueryStr = Cryptor.DecryptText(encryptedQueryStr, Session.SessionID.Substring(0, 8));
                    else if (IsSecureQueryStringStatic) decryptedQueryStr = Cryptor.DecryptText(encryptedQueryStr, STATIC_KEY);

                    if (decryptedQueryStr == "")
                    {
                        _IsIntruderDetected = true;
                        RaiseIntruderEvent(IntruderType.SecureQueryString);
                    }
                    else
                    {
                        SecureQueryString = HttpUtility.ParseQueryString(decryptedQueryStr);
                    }
                }
                catch
                {
                    _IsIntruderDetected = true;
                    RaiseIntruderEvent(IntruderType.SecureQueryString);
                }
            }

            OnPagePreInit();

            if (_PageRequest == null) 
                _PageRequest = Request;

            CheckPagePermission();
        }

        protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument)
        {
            if (this.Request.UrlReferrer == null
                || (Session["sec_key_" + GetType().FullName] == null)
                || (ViewState["sec_key"] == null)
                || Session["sec_key_" + GetType().FullName].ToString() != ViewState["sec_key"].ToString()
                )
            {
                _IsIntruderDetected = true;
                RaiseIntruderEvent(IntruderType.CSRF);
                return;
            }
            base.RaisePostBackEvent(sourceControl, eventArgument);
        }

        protected override bool OnBubbleEvent(object sender, EventArgs e)
        {
            return _IsIntruderDetected;
        }

        protected override void OnLoad(EventArgs e)
        {
            if (!IsPostBack)
            {
                ViewState["sec_key"] = Session["sec_key_" + GetType().FullName] = CryptoServices.Current.SHA1(DateTime.Now.Ticks.ToString());
            }

            if (_IsIntruderDetected) return;
            base.OnLoad(e);
            switch (_PageType)
            {
                case GuruPageType.Normal:
                    ClientScript.RegisterStartupScript(GetType(), "sec_redirect_script", "if(window != parent.window) parent.window.location='" + EncodeHexJSString(Request.RawUrl) + "';", true);
                    break;
                case GuruPageType.Modal:
                    ClientScript.RegisterStartupScript(GetType(), "sec_redirect_script", "if(window == parent.window) parent.window.location='" + EncodeHexJSString(LoginUrl) + "';", true);
                    break;
            }


        }

        private static string EncodeHexJSString(string s)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in s)
            {
                sb.Append("\\x" + ((int)c).ToString("X"));
            }
            return sb.ToString();
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            if (IsNoCache)
            {
                this.Response.AddHeader("Pragma", "no-cache");
                this.Response.AddHeader("Cache-Control", "no-store");
            }
        }

        protected override void OnUnload(EventArgs e)
        {
            base.OnUnload(e);
            OnAspxWriteLog();
        }

        public override void ProcessRequest(HttpContext context)
        {
            if (_PageRequest == null) 
                _PageRequest = context.Request;
            base.ProcessRequest(context);

        }

        #endregion

        #region abstract methods
        
        protected abstract void OnPagePreInit();
        protected abstract void OnPageInit();
        protected abstract void OnAspxWriteLog();
        protected abstract void OnIntruderWriteLog();
        protected abstract void DoIntruderDetected(IntruderType type);

        #endregion

        #region private methods
        public string CurrentLanguage
        {
            get
            {
                if (Session["Guru.Web.UI.GuruPage.CurrentLanguage"] == null)
                {
                    Session["Guru.Web.UI.GuruPage.CurrentLanguage"] = "vn";

                }
                return (string)Session["Guru.Web.UI.GuruPage.CurrentLanguage"];
            }
            set
            {
                Session["Guru.Web.UI.GuruPage.CurrentLanguage"] = value;                
            }
        }
        public string LoginUrl
        {
            get
            {
                HttpCookie url_cokkie = HttpContext.Current.Request.Cookies["login_url"];
                if (url_cokkie != null)
                {
                    return url_cokkie.Value;
                }
                else
                {
                    return FormsAuthentication.LoginUrl;
                }
            }
            set
            {
                HttpCookie c = new HttpCookie("login_url", value);
                c.Path = Request.ApplicationPath;
                HttpContext.Current.Response.Cookies.Add(c);
            }
        }

        protected void CheckPagePermission()
        {
            System.Reflection.MemberInfo info = this.GetType();
            object[] attributes = info.GetCustomAttributes(true);
            for (int i = 0; i < attributes.Length; i++)
            {
                if (attributes[i] is PagePermission)
                {
                    PagePermission _accessPermission = (PagePermission)attributes[i];
                    string[] permissions = _accessPermission.PermissionName.Split(',');
                    bool hasPermission = false;
                    for (int j = 0; j < permissions.Length; j++)
                    {
                        if (IsUserWithPermission(permissions[j]))
                        {
                            hasPermission = true;
                        }
                    }
                    if (!hasPermission)
                    {
                        _IsIntruderDetected = true;
                        RaiseIntruderEvent(IntruderType.Permission);
                    }

                }
            }
        }

        private void RaiseIntruderEvent(IntruderType type)
        {
            OnIntruderWriteLog();
            DoIntruderDetected(type);
        }

        #endregion
       
    }

    public enum IntruderType
    {
        Permission,
        QueryString,
        SecureQueryString,
        Control,
        PostData,
        CSRF,
        Data,
        Unknown
    }

    public class PagePermission : System.Attribute
    {
        public string _permission;
        public PagePermission(string permission)
        {
            this._permission = permission;
        }

        public string PermissionName
        {
            get
            {
                return _permission;
            }
        }
    }

    public class PageTextResource : System.Attribute
    {
        public string _resourceKey;
        public PageTextResource(string key)
        {
            this._resourceKey = key;
        }

        public string ResourceKey
        {
            get
            {
                return _resourceKey;
            }
        }
    }
}
